﻿using System;
using System.Collections.Generic;
using System.Linq;
using Diploma.Models.IRepositories;
using Diploma.Models.Utils.PasswordsUtils;

namespace Diploma.Models.Repositories
{
    internal class LecturersRepository : ILecturersRepository
    {
        private readonly DiplomaEntities dataContext;

        public LecturersRepository(DiplomaEntities dataContext)
        {
            this.dataContext = dataContext;
        }

        public IList<Lecturer> GetAllLecturers()
        {
            return
                dataContext.Users.OfType<Lecturer>().Where(l => l.DeletionDate == null).OrderBy(g => g.Surname).ThenBy(
                    g => g.Name).ThenBy(g => g.Patronymic).ToList();
        }

        public Lecturer GetLecturer(Guid id)
        {
            return
                dataContext.Users.OfType<Lecturer>().FirstOrDefault(u => u.UserId == id && u.DeletionDate == null);
        }

        public IList<Lecturer> GetConsultants(Guid lecturerId)
        {
            return (from lecturer in dataContext.Users.OfType<Lecturer>()
                    where lecturer.UserId != lecturerId
                          && lecturer.DeletionDate == null
                    select lecturer).OrderBy(g => g.Surname).ThenBy(g => g.Name).ThenBy(g => g.Patronymic).ToList();
        }

        public void Add(Lecturer lecturer)
        {
            lecturer.UserId = Guid.NewGuid();
            lecturer.Password = PasswordGenerator.Encrypt(PasswordGenerator.GeneratePassword());

            dataContext.AddToUsers(lecturer);
            dataContext.SaveChanges();
        }

        public void Update(Lecturer newLecturer)
        {
            Lecturer oldLecturer = GetLecturer(newLecturer.UserId);
            if (oldLecturer == null)
                throw new Exception();
            oldLecturer.Rank = newLecturer.Rank;
            oldLecturer.Name = newLecturer.Name;
            oldLecturer.Surname = newLecturer.Surname;
            oldLecturer.Patronymic = newLecturer.Patronymic;
            oldLecturer.LecturersLoadings.Clear();
            oldLecturer.Email = newLecturer.Email;

            foreach (LecturersLoading newLoading in newLecturer.LecturersLoadings)
            {
                oldLecturer.LecturersLoadings.Add(new LecturersLoading
                                                      {
                                                          LecturerId = oldLecturer.UserId,
                                                          StudentsAmount = newLoading.StudentsAmount,
                                                          QualificationLevelId = newLoading.QualificationLevelId,
                                                      });
            }

            dataContext.SaveChanges();
        }

        public void Delete(Lecturer lecturer)
        {
            lecturer.MarkForDeletion();
            dataContext.SaveChanges();
        }

        public Dictionary<Lecturer, IEnumerable<CollaborationRoom>> GetFreeThemes(Student student)
        {
            var list = new Dictionary<Lecturer, IEnumerable<CollaborationRoom>>();

            QualificationLevel level = student.StudentsGroup.QualificationLevel;

            foreach (Lecturer lecturer in GetAllLecturers())
            {
                int loading = 0;
                LecturersLoading loadingObject =
                    lecturer.LecturersLoadings.FirstOrDefault(l => l.QualificationLevel == level && l.DeletionDate == null);
                if (loadingObject != null)
                    loading = loadingObject.StudentsAmount;
                if (loading != 0)
                {
                    int occupied =
                        lecturer.CollaborationRooms.Count(r => r.Student != null && r.Student.StudentsGroup.QualificationLevel == level &&
                                                               r.DeletionDate == null);

                    if (occupied < loading)
                    {
                        var freeThemes =
                            lecturer.CollaborationRooms.Where(r => r.Student == null && r.DeletionDate == null);
                        list.Add(lecturer, freeThemes.OrderBy(g => g.BaseTheme));
                    }
                }
            }

            return list;
        }

        public Dictionary<QualificationLevel, List<CollaborationRoom>> GetGroupedApprovedThemes(Lecturer lecturer)
        {
            IEnumerable<CollaborationRoom> themes =
                lecturer.CollaborationRooms.Where(r => r.Student != null && r.DeletionDate == null);
            IEnumerable<IGrouping<QualificationLevel, CollaborationRoom>> grouped = from t in themes
                                                                                    group t by
                                                                                        t.Student.StudentsGroup.
                                                                                        QualificationLevel
                                                                                    into g
                                                                                    select g;
            Dictionary<QualificationLevel, List<CollaborationRoom>> v = grouped.ToDictionary(g => g.Key, g => g.ToList());
            return v;
        }


        public IEnumerable<CollaborationRoom> GetAllCollaborationRooms(Lecturer lecturer)
        {
            return lecturer.CollaborationRooms.Where(r => r.DeletionDate == null).OrderBy(r => r.BaseTheme);
        }

        public IEnumerable<CollaborationRoom> GetConsultingCollborationRooms(Lecturer lecturer)
        {
            return lecturer.ConsultingCollaborationRooms.Where(r => r.DeletionDate == null).OrderBy(r => r.BaseTheme);
        }

        public IEnumerable<Lecturer> GetDeletedLecturers()
        {
            return dataContext.Users.OfType<Lecturer>().Where(l => l.DeletionDate != null);
        }

        public void Restore(Guid lecturerId)
        {
            Lecturer deletedLecturer = GetDeletedLecturers().FirstOrDefault(l => l.UserId == lecturerId);
            if (deletedLecturer == null)
                throw new Exception("Lecturer not found");
            deletedLecturer.UnmarkFromDeletion();
            dataContext.SaveChanges();
        }

        public IEnumerable<LecturersForPractice> GetLevelsInfo(Guid lecturerId)
        {
            return dataContext.LecturersForPractices.Where(r => r.LecturerId == lecturerId);
        }

        public IEnumerable<Lecturer> GetLecturersForPractice()
        {
            var lecturersIds = dataContext.LecturersForPractices.Select(r => r.LecturerId).Distinct();
            var result = new List<Lecturer>();
            foreach (Guid id in lecturersIds)
            {
                result.Add(GetLecturer(id));
            }
            return result;
        }

        public void AddForPractice(Guid lecturerId)
        {
            var bakalavr = DataManager.Instance.QualificationLevelsRepository.GetLevel(1);
            var spec = DataManager.Instance.QualificationLevelsRepository.GetLevel(3);

            var bakalavrModel = new LecturersForPractice
                                    {
                                        Id = Guid.NewGuid(),
                                        LecturerId = lecturerId,
                                        QualificationLevelId = bakalavr.QualificationLevelId,
                                        MaxSudents = 0
                                    };

            var specModel = new LecturersForPractice
            {
                Id = Guid.NewGuid(),
                LecturerId = lecturerId,
                QualificationLevelId = spec.QualificationLevelId,
                MaxSudents = 0
            };

            dataContext.LecturersForPractices.AddObject(bakalavrModel);
            dataContext.LecturersForPractices.AddObject(specModel);
            dataContext.SaveChanges();
        }

        public void RemoveForPractice(Guid lecturerId)
        {
            foreach (LecturersForPractice item in dataContext.LecturersForPractices.Where(r => r.LecturerId == lecturerId))
            {
                dataContext.LecturersForPractices.DeleteObject(item);
            }
            foreach (Student student in dataContext.Users.OfType<Student>())
            {
                if (student.CollaborationRoom != null && student.CollaborationRoom.PracticeLecturer == lecturerId)
                {
                    student.CollaborationRoom.PracticeLecturer = null;
                }
            }
            dataContext.SaveChanges();
        }

        public void UpdateLecturerLoadingForPractice(Guid lecturerId, int levelId, int value)
        {
            var model = dataContext.LecturersForPractices
                            .FirstOrDefault(r => r.LecturerId == lecturerId && r.QualificationLevelId == levelId);
            if(model != null)
            {
                model.MaxSudents = value;
                dataContext.SaveChanges();
            }
        }
    }
}